package jastgen.generator;

import static jastgen.generator.Helper.*;
import java.io.PrintWriter;
import java.util.List;
import java.util.Set;

import jastgen.CompileException;
import jastgen.nodes.ClassDef;
import jastgen.nodes.Field;
import jastgen.nodes.Module;
import jastgen.nodes.Type;

class VoidVisitorAdapterBodyGenerator implements BodyGenerator {

	private final Set<String> nodeTypes;
	private final Module node;

	VoidVisitorAdapterBodyGenerator(
			final Set<String> nodeTypes,
			final Module node
		) throws CompileException {

		this.nodeTypes = nodeTypes;
		this.node = node;
	}

	@Override
	public void generate(
			final PrintWriter out)
	{
		generateVisitorBody(out, node);
	}

	private void generateVisitorBody(
			final PrintWriter out,
			final Module node)
	{
		out.println("public abstract class VoidVisitorAdapter extends VoidVisitorImpl {");

		// クラスごとのメソッドを出力
		for(final ClassDef c : node.getClassDefs()) {
			out.println();
			generateVisitorBodyClass1(out, c);
		}

		// クラスごとのメソッドを出力
		for(final ClassDef c : node.getClassDefs()) {
			out.println();
			generateVisitorBodyClass2(out, c);
		}

		out.println("}");
	}

	private void generateVisitorBodyClass1(
			final PrintWriter out,
			final ClassDef node)
	{
		final String n = node.getBaseName();
		final List<Field> ms = node.getFields();

		printVisitC(out, node, "in");
		out.println("	{");
		out.println("		enter" + n + "(node);");
		out.println();

		boolean printed = false;
		for(final Field f : ms) {
			printed |= generateVisitorBodyMember(out, f);
		}
		if(printed)
			out.println();

		out.println("		exit" + n + "(node);");
		out.println("	}");
	}

	static boolean importCollection(
			final Set<String> nodeTypes,
			final Module node)
	{
		for(final ClassDef c : node.getClassDefs())
			for(final Field f : c.getFields())
				if(importCollection(nodeTypes, f))
					return true;
		return false;
	}

	// ノードクラスを要素とするコンテナならばtrue
	//
	// VoidVisitorAdapter、NodeReplacer、TreePrinterの3つは大部分が共通
	private static boolean importCollection(
			final Set<String> nodeTypes,
			final Field node)
	{
		final Type type = node.getType();
		if(type.maybeContainerToVisit()) {
			final String inner = type.getOnlyParam().toString();
			if(nodeTypes.contains(inner)) {
				// 型パラメータが1つで、ノードクラスであり、
				// 外側のクラスはcontainerPrefixesに含まれる場合。
				return true;
			}
		}
		return false;
	}

	// VoidVisitorImpl、VoidVisitorAdapter、VisitorImpl、NodeReplacer、TreePrinterの5つは大部分が共通
	private boolean generateVisitorBodyMember(
			final PrintWriter out,
			final Field node)
	{
		final Type type = node.getType();
		final String getX = "node.get" + node.getNameInMethod() + "()";

		if(type.maybeContainerToVisit()) {
			final String inner = type.getOnlyParam().toString();
			if(nodeTypes.contains(inner)) {
				// 型パラメータが1つで、ノードクラスであり、
				// 外側のクラスはcontainerPrefixesに含まれる場合。
				out.println("		inNode(" + getX + ");");
				return true;
			}
		}

		if(nodeTypes.contains(type.toString())) {
			out.println("		inNode(" + getX + ");");
			return true;
		}

		return false;
	}

	private void generateVisitorBodyClass2(
			final PrintWriter out,
			final ClassDef node)
	{
		generateVisitorBodyClass3(out, node, "enter");
		out.println();
		generateVisitorBodyClass3(out, node, "exit");
	}

	private void generateVisitorBodyClass3(
			final PrintWriter out,
			final ClassDef node,
			final String prefix)
	{
		printVisitC(out, node, prefix, "void", false);
		out.println("	{");
		out.println("	}");
	}
}
